import { Meta } from '@storybook/addon-docs'
import LinkTo from '@storybook/addon-links/react'

<Meta title="Guides/Creating Custom Views" />

# Creating A Custom View

It is possible to override an existing Calendar `view`, or even create a new `view` all together, by adhering to a specific interface.

<LinkTo kind="examples" story="example-9">
  View An Example
</LinkTo>

First build your custom `view` component. This could be based off an existing component, or something completely custom.

```js
import React, {useMemo} from 'react'
import PropTypes from 'prop-types'
import { Navigate } from 'react-big-calendar'
import TimeGrid from 'react-big-calendar/lib/TimeGrid'

export default function CustomWeekView({
  date,
  localizer,
  max = localizer.endOf(new Date(), 'day'),
  min = localizer.startOf(new Date(), 'day'),
  scrollToTime = localizer.startOf(new Date(), 'day'),
  ...props
}) {
  const currRange = useMemo(
    () => CustomWeekView.range(date, { localizer }),
    [date, localizer]
  )

  return (
    <TimeGrid
      date={date}
      eventOffset={15}
      localizer={localizer}
      max={max}
      min={min}
      range={currRange}
      scrollToTime={scrollToTime}
      {...props}
    />
  )
}

CustomWeekView.propTypes = {
  date: PropTypes.instanceOf(Date).isRequired,
  localizer: PropTypes.object,
  max: PropTypes.instanceOf(Date),
  min: PropTypes.instanceOf(Date),
  scrollToTime: PropTypes.instanceOf(Date),
}

CustomWeekView.range = (date, { localizer }) => {
  const start = date
  const end = localizer.add(start, 2, 'day')

  let current = start
  const range = []

  while (localizer.lte(current, end, 'day')) {
    range.push(current)
    current = localizer.add(current, 1, 'day')
  }

  return range
}

CustomWeekView.navigate = (date, action, { localizer }) => {
  switch (action) {
    case Navigate.PREVIOUS:
      return localizer.add(date, -3, 'day')

    case Navigate.NEXT:
      return localizer.add(date, 3, 'day')

    default:
      return date
  }
}

CustomWeekView.title = (date, { localizer }) => {
  const [start, ...rest] = CustomWeekView.range(date, { localizer })
  return localizer.format({ start, end: rest.pop() }, 'dayRangeHeaderFormat')
}
```

Pay special attention to the additional static methods attached to your component. As static methods, they have no concept of `this`, and have specific arguments that will be passed into them.

```js
interface View {
  static title(date: Date, { formats: DateFormat[], culture: string?, ...props }): string
  static navigate(date: Date, action: 'PREV' | 'NEXT' | 'DATE'): Date
  static range?(date: Date, { localizer: DateLocalizer, ...props }): array
}
```

To implement your custom `view`, include it in your <LinkTo kind="props" story="views">views</LinkTo> prop.

```js
import CustomWeekView from './CustomWeekView'
//...
const {views, ...otherProps} = useMemo(() => ({
  views: {
    month: true,
    week: CustomWeekView,
    day: true
  },
  // ... other props
}), [])
//...
<Calendar views={views} {...otherProps} />
```
